#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""MySQLPass专业安装程序构建脚本
用于创建包含以下功能的Windows安装程序：
1. 选择安装目录
2. 抽取配置文件
3. 配置程序运行所需的Python环境
"""

import os
import sys
import subprocess
import shutil
import argparse
import platform
import stat
import time
from datetime import datetime

# 确保脚本在正确的目录下运行
script_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(script_dir)

# 定义项目相关路径
PROJECT_NAME = "MySQLPass"
MAIN_SCRIPT = "main.py"
OUTPUT_DIR = os.path.join("..", "build_output")
REQUIREMENTS_FILE = "requirements.txt"
CONFIG_TEMPLATE_DIR = os.path.join("src", "data", "config_templates")

# 定义打包参数
BUILD_NAME = "MySQLPass"
BUILD_VERSION = "1.0.0"

# Inno Setup相关配置
INNO_SETUP_COMPILER = "iscc.exe"  # Inno Setup编译器
INNO_SCRIPT_NAME = "mysqlpass_installer.iss"

# Python环境配置
PYTHON_INSTALLER_URL = "https://www.python.org/ftp/python/3.11.5/python-3.11.5-amd64.exe"  # Python 3.11.5安装包下载链接
PYTHON_VERSION = "3.11.5"
PYTHON_INSTALLER_FILE = f"python-{PYTHON_VERSION}-amd64.exe"


def check_pyinstaller():
    """检查PyInstaller是否已安装"""
    try:
        subprocess.run([sys.executable, "-m", "PyInstaller", "--version"], 
                       check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        return True
    except (subprocess.CalledProcessError, FileNotFoundError):
        return False


def install_pyinstaller():
    """安装PyInstaller"""
    print("正在安装PyInstaller...")
    try:
        subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller"], 
                      check=True)
        print("PyInstaller安装成功")
        return True
    except subprocess.CalledProcessError:
        print("PyInstaller安装失败")
        return False


def clean_build_dir():
    """清理之前的构建目录"""
    print("清理之前的构建文件...")
    # 清理PyInstaller生成的目录
    for dir_name in ["build", "dist"]:
        dir_path = os.path.join(script_dir, dir_name)
        if os.path.exists(dir_path):
            # 确保所有文件都可写
            for root, dirs, files in os.walk(dir_path):
                for file in files:
                    try:
                        os.chmod(os.path.join(root, file), stat.S_IWUSR)
                    except:
                        pass
            shutil.rmtree(dir_path)
    
    # 清理spec文件
    for file in os.listdir(script_dir):
        if file.endswith(".spec"):
            try:
                os.chmod(os.path.join(script_dir, file), stat.S_IWUSR)
                os.remove(os.path.join(script_dir, file))
            except:
                pass
    
    # 清理输出目录
    if os.path.exists(OUTPUT_DIR):
        # 确保所有文件都可写
        for root, dirs, files in os.walk(OUTPUT_DIR):
            for file in files:
                try:
                    os.chmod(os.path.join(root, file), stat.S_IWUSR)
                except:
                    pass
        shutil.rmtree(OUTPUT_DIR)


def create_output_dir():
    """创建输出目录"""
    os.makedirs(OUTPUT_DIR, exist_ok=True)


def create_config_templates():
    """创建配置文件模板目录和示例配置文件"""
    print("创建配置文件模板...")
    
    # 确保配置模板目录存在
    os.makedirs(CONFIG_TEMPLATE_DIR, exist_ok=True)
    
    # 创建主配置文件模板
    main_config = """
# MySQLPass 主配置文件
# 此文件将在安装时复制到用户配置目录

[general]
# 日志级别: DEBUG, INFO, WARNING, ERROR
log_level = INFO

# 界面语言: zh_CN, en_US
i18n_language = zh_CN

# 是否自动检查更新
auto_check_updates = true

# 更新服务器地址
update_server_url = http://localhost:8080

[security]
# 是否加密存储密码
encrypt_passwords = true

# 加密密钥存储位置 (仅在需要时修改)
# key_store_path = 
"""
    
    with open(os.path.join(CONFIG_TEMPLATE_DIR, "main_config.ini"), "w", encoding="utf-8") as f:
        f.write(main_config.strip())
    
    # 创建数据库连接模板文件
    db_template = """
# MySQL 连接模板
# 此文件用于存储数据库连接配置示例

[default]
host = localhost
port = 3306
user = root
password = 
# 可以选择保存密码或每次输入
# save_password = false

# 连接参数
# timeout = 30
# charset = utf8mb4
# ssl_mode = DISABLED
"""
    
    with open(os.path.join(CONFIG_TEMPLATE_DIR, "db_connection.ini.example"), "w", encoding="utf-8") as f:
        f.write(db_template.strip())
    
    print(f"已创建配置文件模板在 {CONFIG_TEMPLATE_DIR}")


def build_with_pyinstaller():
    """使用PyInstaller打包应用"""
    print(f"开始打包{PROJECT_NAME}项目...")
    
    # 构建PyInstaller命令参数
    cmd = [
        sys.executable,
        "-m",
        "PyInstaller",
        "--name", BUILD_NAME,
        "--windowed",  # 不显示控制台窗口
        "--icon", os.path.join("src", "data", "mp.png"),  # 使用mp.png作为图标
        "--collect-all", "pymysql",  # 收集所有pymysql依赖
        "--collect-all", "cryptography",  # 收集所有cryptography依赖
        "--collect-all", "click",  # 收集所有click依赖
        "--collect-all", "prettytable",  # 收集所有prettytable依赖
        "--collect-all", "pandas",  # 收集所有pandas依赖
        "--collect-all", "openpyxl",  # 收集所有openpyxl依赖
        "--collect-all", "xlwt",  # 收集所有xlwt依赖
        "--collect-all", "requests",  # 收集所有requests依赖
        "--add-data", f"src{os.pathsep}src",
        "--add-data", f"README.md{os.pathsep}.",
        "--add-data", f"README.en.md{os.pathsep}.",
        "--add-data", f"requirements.txt{os.pathsep}.",
        "--noupx",  # 禁用UPX压缩，避免DLL问题
    ]
    
    # 如果需要包含updates目录
    if os.path.exists("updates"):
        cmd.extend(["--add-data", f"updates{os.pathsep}updates"])
    
    # 添加主脚本
    cmd.append(MAIN_SCRIPT)
    
    # 执行打包命令
    try:
        subprocess.run(cmd, check=True)
        print("打包成功")
        return True
    except subprocess.CalledProcessError as e:
        print(f"打包失败: {e}")
        return False


def download_python_installer():
    """下载Python安装程序"""
    if not platform.system() == "Windows":
        return None
    
    print(f"正在下载Python {PYTHON_VERSION}安装程序...")
    python_installer_path = os.path.join(OUTPUT_DIR, PYTHON_INSTALLER_FILE)
    
    try:
        import requests
        response = requests.get(PYTHON_INSTALLER_URL, stream=True)
        response.raise_for_status()
        
        with open(python_installer_path, 'wb') as f:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)
        
        print(f"Python安装程序下载成功: {python_installer_path}")
        return python_installer_path
    except Exception as e:
        print(f"Python安装程序下载失败: {e}")
        # 继续执行，但会在Inno Setup脚本中提示用户手动安装Python
        return None


def create_virtual_env_script():
    """创建虚拟环境和安装依赖的脚本"""
    if not platform.system() == "Windows":
        return None
    
    print("创建虚拟环境配置脚本...")
    
    venv_script = f'''
@echo off
setlocal enabledelayedexpansion

rem MySQLPass Python环境配置脚本
rem 此脚本用于创建Python虚拟环境并安装必要的依赖

rem 设置UTF-8编码
chcp 65001 > nul

rem 获取Python安装路径
set "PYTHON_EXE=%~1"
set "INSTALL_DIR=%~2"
set "VENV_DIR=%INSTALL_DIR%\python_env"
set "REQUIREMENTS_FILE=%INSTALL_DIR%\requirements.txt"

rem 检查参数
if "%PYTHON_EXE%"=="" (
    echo 错误: 未提供Python解释器路径
    exit /b 1
)

if "%INSTALL_DIR%"=="" (
    echo 错误: 未提供安装目录
    exit /b 1
)

rem 检查Python是否存在
if not exist "%PYTHON_EXE%" (
    echo 错误: Python解释器不存在于 %PYTHON_EXE%
    exit /b 1
)

rem 检查requirements.txt是否存在
if not exist "%REQUIREMENTS_FILE%" (
    echo 错误: requirements.txt文件不存在于 %REQUIREMENTS_FILE%
    exit /b 1
)

rem 创建虚拟环境
 echo 创建Python虚拟环境...
"%PYTHON_EXE%" -m venv "%VENV_DIR%"
if errorlevel 1 (
    echo 错误: 创建虚拟环境失败
    exit /b 1
)

rem 激活虚拟环境
call "%VENV_DIR%\Scripts\activate"
if errorlevel 1 (
    echo 错误: 激活虚拟环境失败
    exit /b 1
)

rem 升级pip
 echo 升级pip...
python -m pip install --upgrade pip
if errorlevel 1 (
    echo 警告: pip升级失败，但继续执行
)

rem 安装依赖
 echo 安装项目依赖...
python -m pip install -r "%REQUIREMENTS_FILE%"
if errorlevel 1 (
    echo 错误: 安装依赖失败
    exit /b 1
)

rem 复制配置文件到用户目录
 echo 复制配置文件到用户目录...
set "USER_CONFIG_DIR=%APPDATA%\MySQLPass\config"
if not exist "%USER_CONFIG_DIR%" mkdir "%USER_CONFIG_DIR%" -p

rem 复制配置模板
copy /y "%INSTALL_DIR%\src\data\config_templates\*" "%USER_CONFIG_DIR%" > nul
if errorlevel 1 (
    echo 警告: 复制配置文件失败，但继续执行
)

rem 创建日志目录
set "USER_LOG_DIR=%APPDATA%\MySQLPass\logs"
if not exist "%USER_LOG_DIR%" mkdir "%USER_LOG_DIR%" -p

rem 创建启动脚本
 echo 创建启动脚本...
set "START_SCRIPT=%INSTALL_DIR%\start_mysqlpass.bat"
(
    echo @echo off
    echo setlocal enabledelayedexpansion
    echo 
    echo rem MySQLPass启动脚本
    echo rem 此脚本使用虚拟环境运行MySQLPass
    echo 
    echo rem 激活虚拟环境
    echo call "%VENV_DIR%\Scripts\activate"
    echo if errorlevel 1 (
    echo     echo 错误: 激活虚拟环境失败
    echo     pause
    echo     exit /b 1
    echo )
    echo 
    echo rem 运行主程序
    echo cd /d "%INSTALL_DIR%"
    echo python "%INSTALL_DIR%\main.py" --gui
    echo 
    echo rem 暂停以便查看错误
    echo if errorlevel 1 (
    echo     echo 程序运行出错，请查看日志获取详细信息
    echo     pause
    echo )
    echo 
    echo endlocal
) > "%START_SCRIPT%"

rem 显示完成信息
 echo 
 echo ==================================================
 echo Python环境配置成功!
 echo 虚拟环境位置: %VENV_DIR%
 echo 启动脚本: %START_SCRIPT%
 echo 用户配置目录: %USER_CONFIG_DIR%
 echo ==================================================

exit /b 0
'''
    
    venv_script_path = os.path.join(OUTPUT_DIR, "setup_python_env.bat")
    with open(venv_script_path, "w", encoding="utf-8") as f:
        f.write(venv_script.strip())
    
    # 设置脚本为可执行
    os.chmod(venv_script_path, os.stat(venv_script_path).st_mode | stat.S_IEXEC)
    
    print(f"已创建虚拟环境配置脚本: {venv_script_path}")
    return venv_script_path


def create_inno_setup_script():
    """创建Inno Setup脚本"""
    if not platform.system() == "Windows":
        return None
    
    print("创建Inno Setup安装脚本...")
    
    # 检查Python安装程序是否存在
    python_installer_path = os.path.join(OUTPUT_DIR, PYTHON_INSTALLER_FILE)
    python_installer_exists = os.path.exists(python_installer_path)
    
    inno_script = f'''
; MySQLPass 安装程序脚本
; 使用Inno Setup创建专业的Windows安装程序

[Setup]
AppName={BUILD_NAME}
AppVersion={BUILD_VERSION}
AppPublisher=MySQLPass Team
AppPublisherURL=http://localhost
AppSupportURL=http://localhost/support
AppUpdatesURL=http://localhost/updates
DefaultDirName={{autopf}}\\{BUILD_NAME}
DefaultGroupName={BUILD_NAME}
OutputBaseFilename={BUILD_NAME}-Setup-{BUILD_VERSION}
Compression=lzma
SolidCompression=yes
WizardStyle=modern
UninstallDisplayIcon={{app}}\\src\\data\\mp.png
UninstallDisplayName={BUILD_NAME} v{BUILD_VERSION}

[Languages]
Name: "chinese"; MessagesFile: "compiler:Default.isl"
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
Name: "createpythonenv"; Description: "创建Python虚拟环境"; GroupDescription: "Python环境配置"; Flags: checked
Name: "installpython"; Description: "安装Python {PYTHON_VERSION}"; GroupDescription: "Python环境配置"; Flags: unchecked; {"" if python_installer_exists else "Check: not IsPythonInstalled();"}

[Files]
; 主程序文件
Source: "dist\\{BUILD_NAME}\\{BUILD_NAME}.exe"; DestDir: "{{app}}"; Flags: ignoreversion

; 项目文件
Source: "dist\\{BUILD_NAME}\\*"; DestDir: "{{app}}"; Flags: ignoreversion recursesubdirs createallsubdirs

; 安装脚本
Source: "{OUTPUT_DIR}\\setup_python_env.bat"; DestDir: "{{app}}"; Flags: ignoreversion

; README和文档
Source: "README.md"; DestDir: "{{app}}"; Flags: ignoreversion
Source: "README.en.md"; DestDir: "{{app}}"; Flags: ignoreversion

; 配置模板
Source: "{CONFIG_TEMPLATE_DIR}\\*"; DestDir: "{{app}}\\src\\data\\config_templates"; Flags: ignoreversion recursesubdirs createallsubdirs

; Python安装程序 (如果存在)
{"Source: \"%s\"; DestDir: \"{tmp}\"; Flags: ignoreversion" % python_installer_path if python_installer_exists else ""}

[Icons]
Name: "{{group}}\\{BUILD_NAME}"; Filename: "{{app}}\\start_mysqlpass.bat"
Name: "{{commondesktop}}\\{BUILD_NAME}"; Filename: "{{app}}\\start_mysqlpass.bat"; Tasks: desktopicon

[Run]
; 安装完成后配置Python环境
Filename: "cmd.exe"; Parameters: "/c \"{{app}}\\setup_python_env.bat\" \"{{code:GetPythonExePath}}\" \"{{app}}\""; Description: "配置Python环境..."; Flags: runhidden waituntilterminated; Tasks: createpythonenv

; 安装Python (如果需要)
"Filename: ""{tmp}\\%s""; Parameters: ""/quiet InstallAllUsers=1 PrependPath=1 Include_test=0""; Description: ""安装Python %s...""; Flags: runhidden waituntilterminated; Tasks: installpython" % (PYTHON_INSTALLER_FILE, PYTHON_VERSION) if python_installer_exists else ""

[UninstallRun]
; 卸载时清理用户数据目录
Filename: "cmd.exe"; Parameters: "/c rmdir /s /q \"%APPDATA%\\MySQLPass\""; Flags: runhidden

[Code]
function IsPythonInstalled(): Boolean;
var
  PythonPath: string;
begin
  Result := False;
  PythonPath := ExpandConstant('{sys}\python.exe');
  if FileExists(PythonPath) then
  begin
    Result := True;
  end;
end;

function GetPythonExePath(): string;
var
  PythonPath: string;
begin
  // 尝试从系统路径获取Python
  PythonPath := ExpandConstant('{sys}\python.exe');
  if not FileExists(PythonPath) then
  begin
    // 如果系统中没有Python，尝试使用安装的Python
    PythonPath := ExpandConstant('{pf}\Python{PYTHON_VERSION}\python.exe');
    if not FileExists(PythonPath) then
    begin
      // 如果还是没有找到，返回空字符串
      PythonPath := '';
    end;
  end;
  Result := PythonPath;
end;
'''
    
    inno_script_path = os.path.join(script_dir, INNO_SCRIPT_NAME)
    with open(inno_script_path, "w", encoding="utf-8-sig") as f:  # 使用UTF-8 with BOM确保中文正常显示
        f.write(inno_script.strip())
    
    print(f"已创建Inno Setup脚本: {inno_script_path}")
    return inno_script_path


def compile_inno_setup_script(script_path):
    """编译Inno Setup脚本"""
    if not platform.system() == "Windows":
        return None
    
    print("正在编译Inno Setup脚本...")
    
    try:
        # 检查Inno Setup编译器是否可用
        subprocess.run([INNO_SETUP_COMPILER, "--version"], 
                      check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        
        # 编译脚本
        subprocess.run([INNO_SETUP_COMPILER, script_path], check=True)
        
        # 查找生成的安装程序
        setup_exe = None
        for file in os.listdir(script_dir):
            if file.startswith(f"{BUILD_NAME}-Setup-{BUILD_VERSION}") and file.endswith(".exe"):
                setup_exe = os.path.join(script_dir, file)
                break
        
        if setup_exe and os.path.exists(setup_exe):
            # 移动到输出目录
            shutil.move(setup_exe, OUTPUT_DIR)
            setup_exe = os.path.join(OUTPUT_DIR, os.path.basename(setup_exe))
            print(f"安装程序编译成功: {setup_exe}")
            return setup_exe
        else:
            print("无法找到生成的安装程序")
            return None
    except subprocess.CalledProcessError as e:
        print(f"Inno Setup编译失败: {e}")
        print("请确保Inno Setup已安装并添加到系统PATH中")
        print("或者从 https://jrsoftware.org/isdl.php 下载并安装Inno Setup")
        return None


def main():
    """主函数"""
    parser = argparse.ArgumentParser(description='MySQLPass专业安装程序构建脚本')
    parser.add_argument('--no-download-python', action='store_true', help='不下载Python安装程序')
    parser.add_argument('--skip-pyinstaller', action='store_true', help='跳过PyInstaller打包步骤')
    parser.add_argument('--skip-inno-compile', action='store_true', help='跳过Inno Setup编译步骤')
    
    args = parser.parse_args()
    download_python = not args.no_download_python
    run_pyinstaller = not args.skip_pyinstaller
    compile_inno = not args.skip_inno_compile
    
    # 检查操作系统
    if not platform.system() == "Windows":
        print("错误: 此脚本仅支持Windows系统")
        sys.exit(1)
    
    # 1. 检查并安装PyInstaller
    if run_pyinstaller and not check_pyinstaller():
        if not install_pyinstaller():
            print("无法安装PyInstaller，退出打包")
            sys.exit(1)
    
    try:
        # 2. 清理之前的构建文件
        clean_build_dir()
        
        # 3. 创建配置文件模板
        create_config_templates()
        
        # 4. 创建输出目录
        create_output_dir()
        
        # 5. 使用PyInstaller打包应用
        if run_pyinstaller:
            if not build_with_pyinstaller():
                print("PyInstaller打包失败，退出")
                sys.exit(1)
        else:
            print("跳过PyInstaller打包步骤")
        
        # 6. 下载Python安装程序
        if download_python:
            download_python_installer()
        else:
            print("跳过Python安装程序下载")
        
        # 7. 创建虚拟环境配置脚本
        create_virtual_env_script()
        
        # 8. 创建Inno Setup脚本
        inno_script = create_inno_setup_script()
        
        # 9. 编译Inno Setup脚本
        setup_exe = None
        if compile_inno and inno_script:
            setup_exe = compile_inno_setup_script(inno_script)
        else:
            print("跳过Inno Setup编译步骤")
        
        # 10. 显示完成信息
        print("\nMySQLPass 专业安装程序构建任务已完成!")
        print(f"版本: {BUILD_VERSION}")
        print(f"打包时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        
        if setup_exe:
            print(f"\n安装程序已生成: {setup_exe}")
        else:
            print("\n注意: Inno Setup安装程序未生成，请手动编译:")
            print(f"  1. 安装Inno Setup")
            print(f"  2. 打开 {inno_script}")
            print(f"  3. 点击编译按钮生成安装程序")
        
        print("\n安装程序功能:")
        print("  - 允许用户选择安装目录")
        print("  - 自动抽取和配置程序所需的配置文件")
        print("  - 可选安装Python环境")
        print("  - 自动创建Python虚拟环境并安装依赖")
        print("  - 创建桌面和开始菜单快捷方式")
        print("  - 支持程序卸载和清理")
        
    except Exception as e:
        print(f"构建过程中发生错误: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)
    
if __name__ == "__main__":
    main()